/**
 * 处理客户搜索逻辑
 */
import { getLogger, tuple, cancellable, Service } from '@gnlab/utils'
import type { CancellablePromise, MaySuccess } from '@gnlab/utils'
import { MemSubject } from '@gnlab/utils/rx'
import { OrderedMap } from 'immutable'
import debounce from 'lodash/debounce'
import type { Services } from '@/services'
import { searchInternals } from './api'
import { type InternalContact } from './index'

// 查询条件
export interface InternalSearchConditions {
  wxids?: string[]
  keyword: string
  page: number
}
// 加载状态，loading=promise 代表加载中； loading=null 代表未在加载，同时 finish 代表是否还有下一页
type InternalSearchLoading =
  | { loading: CancellablePromise<MaySuccess<InternalContact[]>> }
  | { loading: null; finish: boolean }
// 已加载进来的外部联系人列表
type InternalSearchItems = { items: OrderedMap<string, InternalContact> }
export type InternalSearching =
  | (InternalSearchConditions & InternalSearchLoading & InternalSearchItems)
  | null

const logger = getLogger('services/internals/search')

export class Search extends Service<Services> {
  private get qywxs() {
    return this.services.qywxs
  }

  registered() {
    this.qywxs.notAlived.subscribe(info => this.excludeQywx(info.wxid))
  }

  protected _searching = new MemSubject<InternalSearching>(null) // 为 null 代表未触发搜索
  searching = this._searching.asStream()

  /**
   * 执行按关键词搜索
   */
  byKeyword = debounce((keyword: string, wxids?: string[]) => {
    void this.query({ keyword, wxids })
  }, 300)

  /**
   * 加载下一页
   */
  async next() {
    return this.query()
  }

  /**
   * 执行搜索
   *
   * 指定了 condition 代表执行一次新的搜索
   * 不指定则为加载下一页
   *
   * 注意：此方法未进行频率控制，需要在上层加以限制
   */
  protected async query(baseConditions?: Omit<InternalSearchConditions, 'page'>) {
    let searching = this.searching.value

    // 前期准备
    if (baseConditions) {
      if (searching?.loading) searching.loading.cancel() // 执行新搜索前，终止之前未完成的搜索
      searching = null
    } else {
      if (!searching) return void logger.error('未指定搜索条件，且没有正在进行的搜索')
      if (searching.loading) return searching.loading // 上次加载还未完成，不再次触发加载
      if ('finish' in searching && searching.finish) return // 已加载到最后一页，不再次触发加载
      baseConditions = {
        keyword: searching.keyword,
        ...('wxids' in searching ? { wxids: searching.wxids } : {}),
      }
    }

    const conditions: InternalSearchConditions = {
      ...baseConditions,
      page: searching ? searching.page + 1 : 1,
    }

    const wxids = conditions.wxids?.length ?? 0 ? conditions.wxids! : this.qywxs.usableWxids.value
    const request = cancellable(searchInternals(wxids, conditions.keyword, conditions.page))

    this._searching.next({
      ...conditions,
      loading: request,
      items: searching?.items ?? OrderedMap(),
    })

    void request.then(res => {
      const current = this.searching.value!
      if (res.success) {
        const items = res.data.length
          ? OrderedMap([...current.items, ...res.data.map(item => tuple(item.id, item))])
          : current.items

        this._searching.next({
          ...current,
          loading: null,
          finish: !res.data.length,
          items,
        })
      } else {
        this._searching.next({
          ...current,
          loading: null,
          finish: false,
          page: current.page - 1, // 加载失败，还原页数
        })
      }
    })

    return request
  }

  /**
   * 清空搜索
   */
  clear() {
    const searching = this.searching.value
    if (searching?.loading) searching.loading.cancel()
    this._searching.next(null)
  }

  /**
   * 从搜索结果中排除指定企业微信
   */
  excludeQywx(wxid: string) {
    const searching = this._searching.value
    if (searching) {
      this._searching.next({
        ...searching,
        items: searching.items.filter(item => item.ownerWxid !== wxid),
      })
    }
  }
}
